<!DOCTYPE html>
<html lang="zh-Hans">
<head>

    <!-- hexo-inject:begin --><!-- hexo-inject:end --><!--[if lt IE 9]>
        <style>body {display: none; background: none !important} </style>
        <meta http-equiv="Refresh" Content="0; url=//outdatedbrowser.com/" />
    <![endif]-->

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="format-detection" content="telephone=no" />
<meta name="author" content="Dragonflyxyz" />



<meta name="description" content="Decorated Concurrency - Python multiprocessing made really really easyfrom:https://www.peterbe.com/plog/decotl;dr There’s a new interesting wrapper on Python multiprocessing called deco, written by Al">
<meta property="og:type" content="article">
<meta property="og:title" content="Python多线程(Decorated concurrency)">
<meta property="og:url" content="https://dragonflyxyz.github.io/2016/05/17/python-decorated-concurrency/index.html">
<meta property="og:site_name" content="Dragonflyxyz的知识整理">
<meta property="og:description" content="Decorated Concurrency - Python multiprocessing made really really easyfrom:https://www.peterbe.com/plog/decotl;dr There’s a new interesting wrapper on Python multiprocessing called deco, written by Al">
<meta property="og:image" content="https://dragonflyxyz.github.io/images/e1ead7c28760d4b30f500fbd2705873f.png">
<meta property="og:image" content="https://dragonflyxyz.github.io/images/0b3b67971b642c1937445ecdf88fcd65.png">
<meta property="og:updated_time" content="2016-10-17T06:25:37.855Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Python多线程(Decorated concurrency)">
<meta name="twitter:description" content="Decorated Concurrency - Python multiprocessing made really really easyfrom:https://www.peterbe.com/plog/decotl;dr There’s a new interesting wrapper on Python multiprocessing called deco, written by Al">
<meta name="twitter:image" content="https://dragonflyxyz.github.io/images/e1ead7c28760d4b30f500fbd2705873f.png">

<link rel="apple-touch-icon" href= "/apple-touch-icon.png">


    <link rel="alternate" href="/atom.xml" title="Dragonflyxyz的知识整理" type="application/atom+xml">



    <link rel="shortcut icon" href="/favicon.png">



    <link href="//cdn.bootcss.com/animate.css/3.5.1/animate.min.css" rel="stylesheet">



    <link href="//cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.min.css" rel="stylesheet">



    <script src="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
    <link href="//cdn.bootcss.com/pace/1.0.2/themes/blue/pace-theme-minimal.css" rel="stylesheet">


<link rel="stylesheet" href="/css/style.css">



<link href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet">


<title>Python多线程(Decorated concurrency) | Dragonflyxyz的知识整理</title>

<script src="//cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
<script src="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script>

<script>
    var yiliaConfig = {
        fancybox: true,
        animate: true,
        isHome: false,
        isPost: true,
        isArchive: false,
        isTag: false,
        isCategory: false,
        fancybox_js: "//cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.min.js",
        scrollreveal: "//cdn.bootcss.com/scrollReveal.js/3.1.4/scrollreveal.min.js",
        search: true
    }
</script>


    <script> yiliaConfig.jquery_ui = [false]; </script>



    <script> yiliaConfig.rootUrl = "\/";</script><!-- hexo-inject:begin --><!-- hexo-inject:end -->






</head>
<body>
  <!-- hexo-inject:begin --><!-- hexo-inject:end --><div id="container">
    <div class="left-col">
    <div class="overlay"></div>
<div class="intrude-less">
    <header id="header" class="inner">
        <a href="/" class="profilepic">
            <img src="/img/avatar.png" class="animated zoomIn">
        </a>
        <hgroup>
          <h1 class="header-author"><a href="/">Dragonflyxyz</a></h1>
        </hgroup>

        

        
            <form id="search-form">
            <input type="text" id="local-search-input" name="q" placeholder="search..." class="search form-control" autocomplete="off" autocorrect="off" searchonload="false" />
            <i class="fa fa-times" onclick="resetSearch()"></i>
            </form>
            <div id="local-search-result"></div>
            <p class='no-result'>No results found <i class='fa fa-spinner fa-pulse'></i></p>
        


        
            <div id="switch-btn" class="switch-btn">
                <div class="icon">
                    <div class="icon-ctn">
                        <div class="icon-wrap icon-house" data-idx="0">
                            <div class="birdhouse"></div>
                            <div class="birdhouse_holes"></div>
                        </div>
                        <div class="icon-wrap icon-ribbon hide" data-idx="1">
                            <div class="ribbon"></div>
                        </div>
                        
                        <div class="icon-wrap icon-link hide" data-idx="2">
                            <div class="loopback_l"></div>
                            <div class="loopback_r"></div>
                        </div>
                        
                        
                        <div class="icon-wrap icon-me hide" data-idx="3">
                            <div class="user"></div>
                            <div class="shoulder"></div>
                        </div>
                        
                    </div>
                    
                </div>
                <div class="tips-box hide">
                    <div class="tips-arrow"></div>
                    <ul class="tips-inner">
                        <li>菜单</li>
                        <li>标签</li>
                        
                        <li>友情链接</li>
                        
                        
                        <li>关于我</li>
                        
                    </ul>
                </div>
            </div>
        

        <div id="switch-area" class="switch-area">
            <div class="switch-wrap">
                <section class="switch-part switch-part1">
                    <nav class="header-menu">
                        <ul>
                        
                            <li><a href="/">主页</a></li>
                        
                            <li><a href="/archives/">所有文章</a></li>
                        
                            <li><a href="/tags/">标签云</a></li>
                        
                            <li><a href="/about/">关于我</a></li>
                        
                        </ul>
                    </nav>
                    <nav class="header-nav">
                        <ul class="social">
                            
                                <a class="fa Email" href="mailto:123@123.com" title="Email"></a>
                            
                                <a class="fa GitHub" href="#" title="GitHub"></a>
                            
                                <a class="fa RSS" href="/atom.xml" title="RSS"></a>
                            
                        </ul>
                    </nav>
                </section>
                
                
                <section class="switch-part switch-part2">
                    <div class="widget tagcloud" id="js-tagcloud">
                        <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/AES/">AES</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/AI/">AI</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Bigdata/">Bigdata</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/C/">C</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/C/">C#</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/C/">C++</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DeepLearning/">DeepLearning</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Hadoop/">Hadoop</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/IOT/">IOT</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Java/">Java</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Linux/">Linux</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/MIT/">MIT</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/MachineLearning/">MachineLearning</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Mathematics/">Mathematics</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Matlab/">Matlab</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Microsoft/">Microsoft</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Node-js/">Node.js</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/OpenCV/">OpenCV</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Python/">Python</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/R/">R</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/RDMS/">RDMS</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/RTOS/">RTOS</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Ruby/">Ruby</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SketchUp/">SketchUp</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Vision/">Vision</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/arduino/">arduino</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/bigdata/">bigdata</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/blog/">blog</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/chip/">chip</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/code/">code</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hexo/">hexo</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/homedesign/">homedesign</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/intel/">intel</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/">mysql</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/opensource/">opensource</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/optimize/">optimize</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/others/">others</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/php/">php</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/picture/">picture</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/python/">python</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/robot/">robot</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/science/">science</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/solution/">solution</a></li></ul>
                    </div>
                </section>
                
                
                
                <section class="switch-part switch-part3">
                    <div id="js-friends">
                    
                      <a class="main-nav-link switch-friends-link" href="https://hexo.io">Hexo</a>
                    
                      <a class="main-nav-link switch-friends-link" href="https://pages.github.com/">GitHub</a>
                    
                      <a class="main-nav-link switch-friends-link" href="http://moxfive.xyz/">MOxFIVE</a>
                    
                    </div>
                </section>
                

                
                
                <section class="switch-part switch-part4">
                
                    <div id="js-aboutme">专注于前端</div>
                </section>
                
            </div>
        </div>
    </header>                
</div>
    </div>
    <div class="mid-col">
      <nav id="mobile-nav">
      <div class="overlay">
          <div class="slider-trigger"></div>
          <h1 class="header-author js-mobile-header hide"><a href="/" title="回到主页">Dragonflyxyz</a></h1>
      </div>
    <div class="intrude-less">
        <header id="header" class="inner">
            <a href="/" class="profilepic">
                <img src="/img/avatar.png" class="animated zoomIn">
            </a>
            <hgroup>
              <h1 class="header-author"><a href="/" title="回到主页">Dragonflyxyz</a></h1>
            </hgroup>
            
            <nav class="header-menu">
                <ul>
                
                    <li><a href="/">主页</a></li>
                
                    <li><a href="/archives/">所有文章</a></li>
                
                    <li><a href="/tags/">标签云</a></li>
                
                    <li><a href="/about/">关于我</a></li>
                
                <div class="clearfix"></div>
                </ul>
            </nav>
            <nav class="header-nav">
                        <ul class="social">
                            
                                <a class="fa Email" target="_blank" href="mailto:123@123.com" title="Email"></a>
                            
                                <a class="fa GitHub" target="_blank" href="#" title="GitHub"></a>
                            
                                <a class="fa RSS" target="_blank" href="/atom.xml" title="RSS"></a>
                            
                        </ul>
            </nav>
        </header>                
    </div>
    <link class="menu-list" tags="标签" friends="友情链接" about="关于我"/>
</nav>
      <div class="body-wrap"><article id="post-python-decorated-concurrency" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/2016/05/17/python-decorated-concurrency/" class="article-date">
      <time datetime="2016-05-17T06:03:48.000Z" itemprop="datePublished">2016-05-17</time>
</a>


    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      Python多线程(Decorated concurrency)
    </h1>
  

      </header>
      
      <div class="article-info article-info-post">
        
    <div class="article-category tagcloud">
    <a class="article-category-link" href="/categories/Programming/">Programming</a>
    </div>


        
    <div class="article-tag tagcloud">
        <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Python/">Python</a></li></ul>
    </div>

        <div class="clearfix"></div>
      </div>
      
    
    <div class="article-entry" itemprop="articleBody">
      
          
        <p>Decorated Concurrency - Python multiprocessing made really really easy<br>from:<a href="https://www.peterbe.com/plog/deco" target="_blank" rel="external">https://www.peterbe.com/plog/deco</a><br>tl;dr There’s a new interesting wrapper on Python multiprocessing called deco, written by Alex Sherman and Peter Den Hartog, both at University of Wisconsin - Madison. It makes Python multiprocessing really really easy.<br><a id="more"></a><br>The paper is here (PDF) and the code is here: <a href="https://github.com/alex-sherman/deco" target="_blank" rel="external">https://github.com/alex-sherman/deco</a>.</p>
<p>This library is based on something called Pydron which, if I understand it correctly, is still a piece of research with no code released. (“We currently estimate that we will be ready for the release in the first quarter of 2015.”)</p>
<p>Apart from using simple decorators on functions, the big difference that deco takes, is that it makes it really easy to get started and that there’s a hard restriction on how to gather the results of sub-process calls’. In deco, you pass in a mutable object that has a keyed index (e.g. a python dict). A python list is also mutable but it doesn’t have an index. Meaning, you could get race conditions on mylist.append().</p>
<p>“However, DECO does impose one important restriction on the program: all mutations may only by index based.”</p>
<p>Some basic example</p>
<p>Just look at this example:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># before.py</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">slow</span><span class="params">(index)</span>:</span></div><div class="line">    time.sleep(<span class="number">5</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">()</span>:</span></div><div class="line">    <span class="keyword">for</span> index <span class="keyword">in</span> list(<span class="string">'123'</span>):</div><div class="line">        slow(index)</div><div class="line">run()</div></pre></td></tr></table></figure>
<p>And when run, you clearly expect it to take 15 seconds:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ time python before.py</div><div class="line"></div><div class="line">real    0m15.090s</div><div class="line">user    0m0.057s</div><div class="line">sys 0m0.022s</div></pre></td></tr></table></figure></p>
<p>Ok, let’s parallelize this with deco. First pip install deco, then:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># after.py</span></div><div class="line"><span class="keyword">from</span> deco <span class="keyword">import</span> concurrent, synchronized</div><div class="line"></div><div class="line"><span class="meta">@concurrent</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">slow</span><span class="params">(index)</span>:</span></div><div class="line">    time.sleep(<span class="number">5</span>)</div><div class="line"></div><div class="line"><span class="meta">@synchronized</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">()</span>:</span></div><div class="line">    <span class="keyword">for</span> index <span class="keyword">in</span> list(<span class="string">'123'</span>):</div><div class="line">        slow(index)</div><div class="line"></div><div class="line">run()</div></pre></td></tr></table></figure></p>
<p>And when run, it should be less than 15 seconds:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ time python after.py</div><div class="line"></div><div class="line">real    0m5.145s</div><div class="line">user    0m0.082s</div><div class="line">sys 0m0.038s</div></pre></td></tr></table></figure></p>
<h3>About the order of execution</h3>

<p>Let’s put some logging into that slow() function above.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">slow</span><span class="params">(index)</span>:</span></div><div class="line">    time.sleep(<span class="number">5</span>)</div><div class="line">    <span class="keyword">print</span> <span class="string">'done with &#123;&#125;'</span>.format(index)</div></pre></td></tr></table></figure></p>
<p>Run the example a couple of times and note that the order is not predictable:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">$ python after.py</div><div class="line"><span class="keyword">done</span> with 1</div><div class="line"><span class="keyword">done</span> with 3</div><div class="line"><span class="keyword">done</span> with 2</div><div class="line">$ python after.py</div><div class="line"><span class="keyword">done</span> with 1</div><div class="line"><span class="keyword">done</span> with 2</div><div class="line"><span class="keyword">done</span> with 3</div><div class="line">$ python after.py</div><div class="line"><span class="keyword">done</span> with 3</div><div class="line"><span class="keyword">done</span> with 2</div><div class="line"><span class="keyword">done</span> with 1</div></pre></td></tr></table></figure></p>
<p>That probably don’t come as a surprise for those familiar with async stuff, but it’s worth reminding so you don’t accidentally depend on order.</p>
<p></p><h3>@synchronized or .wait()</h3><p></p>
<p>Remember the run() function in the example above? The @synchronized decorator is magic. It basically figures out that within the function call there are calls out to sub-process work. What it does it that it “pauses” until all those have finished. An alternative approach is to call the .wait() method on the decorated concurrency function:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">()</span>:</span></div><div class="line">    <span class="keyword">for</span> index <span class="keyword">in</span> list(<span class="string">'123'</span>):</div><div class="line">        slow(index)</div><div class="line">    slow.wait()</div></pre></td></tr></table></figure></p>
<p>That works the same way. This could potentially be useful if you, on the next line, need to depend on the results. But if that’s the case you could just split up the function and slap a @synchronized decorator on the split-out function.</p>
<h3>No Fire-and-forget</h3>

<p>It might be tempting to not set the @synchronized decorator and not call .wait() hoping the work will be finished anyway somewhere in the background. The functions that are concurrent could be, for example, functions that generate thumbnails from a larger image or something time consuming where you don’t care when it finishes, as long as it finishes.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># fireandforget.py</span></div><div class="line"><span class="comment"># THIS DOES NOT WORK</span></div><div class="line"><span class="comment"># And it's not expected to either.</span></div><div class="line"></div><div class="line"><span class="meta">@concurrent</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">slow</span><span class="params">(index)</span>:</span></div><div class="line">    time.sleep(<span class="number">5</span>)</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">()</span>:</span></div><div class="line">    <span class="keyword">for</span> index <span class="keyword">in</span> list(<span class="string">'123'</span>):</div><div class="line">        slow(index)</div><div class="line"></div><div class="line">run()</div></pre></td></tr></table></figure></p>
<p>When you run it, you don’t get an error:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ time python fireandforget.py</div><div class="line"></div><div class="line">real    0m0.231s</div><div class="line">user    0m0.079s</div><div class="line">sys 0m0.047s</div></pre></td></tr></table></figure></p>
<p>But if you dig deeper, you’ll find that it never actually executes those concurrent functions.</p>
<p>If you want to do fire-and-forget you need to have another service/process that actually keeps running and waiting for all work to be finished. That’s how the likes of a message queue works.</p>
<h3>Number of concurrent workers</h3>

<p>multiprocessing.Pool automatically, as far as I can understand, figures out how many concurrent jobs it can run. On my Mac, where I have 8 CPUS, the number is 8.</p>
<p>This is easy to demonstrate. In the example above it does exactly 3 concurrent jobs, because len(list(‘123’)) == 3. If I make it 8 items, the whole demo run takes, still, 5 seconds (plus a tiny amount of overhead). If I make it 9 items, it now takes 10 seconds.</p>
<p>How multiprocessing figures this out I don’t know but I can’t imagine it being anything but a standard lib OS call to ask the operating system how many CPUs it has.</p>
<p>You can actually override this with your own number. It looks like this:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> deco <span class="keyword">import</span> concurrent</div><div class="line"></div><div class="line"><span class="meta">@concurrent(processes=5)</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">really_slow_and_intensive_thing</span><span class="params">()</span>:</span></div><div class="line">    ...</div></pre></td></tr></table></figure></p>
<p>So that way, the operating system doesn’t get too busy. It’s like a throttle.</p>
<h3>A more realistic example</h3>

<p>Let’s actually use the mutable for something and let’s do something that isn’t just a time.sleep(). Also, let’s do something that is CPU bound. A lot of times where concurrency is useful is when you’re network bound because running many network waiting things at the same time doesn’t hose the system from being able to do other things.</p>
<p>Here’s the code:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</div><div class="line"><span class="keyword">from</span> deco <span class="keyword">import</span> concurrent, synchronized</div><div class="line"></div><div class="line"></div><div class="line"><span class="meta">@concurrent</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">slow</span><span class="params">(times, offset)</span>:</span></div><div class="line">    t0 = time.time()</div><div class="line">    path = <span class="string">'9745e8.jpg'</span></div><div class="line">    img = Image.open(path)</div><div class="line">    size = (<span class="number">100</span> + offset * <span class="number">20</span>, <span class="number">100</span> + offset * <span class="number">20</span>)</div><div class="line">    img.thumbnail(size, Image.ANTIALIAS)</div><div class="line">    img.save(<span class="string">'thumbnails/&#123;&#125;.jpg'</span>.format(offset), <span class="string">'JPEG'</span>)</div><div class="line">    t1 = time.time()</div><div class="line">    times[offset] = t1 - t0</div><div class="line"></div><div class="line"><span class="meta">@synchronized</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(times)</span>:</span></div><div class="line">    <span class="keyword">for</span> index <span class="keyword">in</span> range(<span class="number">100</span>):</div><div class="line">        slow(times, index)</div><div class="line"></div><div class="line">t0 = time.time()</div><div class="line">times = &#123;&#125;</div><div class="line">run(times)</div><div class="line">t1 = time.time()</div><div class="line"><span class="keyword">print</span> <span class="string">"TOOK"</span>, t1-t0</div><div class="line"><span class="keyword">print</span> <span class="string">"WOULD HAVE TAKEN"</span>, sum(times.values())</div></pre></td></tr></table></figure></p>
<p>It generates 100 different thumbnails from a very large original JPG. Running this on my macbook pro takes 8.4 seconds but the individual times was a total of 65.1 seconds. The numbers makes sense, because 65 seconds / 8 cores ~= 8 seconds.</p>
<p>But, where it gets really interesting is that if you remove the deco decorators and run 100 thumbnail creations in serial, on my laptop, it takes 28.9 seconds. Now, 28.9 seconds is much more than 8.4 seconds so it’s still a win to multiprocessing for this kind of CPU bound work. However, stampeding herd of doing 8 CPU intensive tasks at the same time can put some serious strains on your system. Also, it could cause high spikes in terms of memory allocation that wouldn’t have happened if freed space can be re-used in the serial pattern.</p>
<p>Here’s by the way the difference in what this looks like in the Activity Monitor:</p>
<p>Fully concurrent PIL work<br><img src="/images/e1ead7c28760d4b30f500fbd2705873f.png" alt="enter description here" title="e1ead7c28760d4b30f500fbd2705873f.png"></p>
<p>Same work but in serial<br><img src="/images/0b3b67971b642c1937445ecdf88fcd65.png" alt="enter description here" title="0b3b67971b642c1937445ecdf88fcd65.png"></p>
<h3>One more “realistic” pattern</h3>

<p>Let’s do this again with a network bound task. Let’s download 100 webpages from my blog. We’ll do this by keeping an index where the URL is the key and the value is the time it took to download that one individual URL. This time, let’s start with the serial pattern:</p>
<p>(Note! I ran these two experiments a couple of times so that the server-side cache would get a chance to clear out outliers)<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> time, requests</div><div class="line"></div><div class="line">urls = <span class="string">"""</span></div><div class="line">https://www.peterbe.com/plog/blogitem-040212-1</div><div class="line">https://www.peterbe.com/plog/geopy-distance-calculation-pitfall</div><div class="line">https://www.peterbe.com/plog/app-for-figuring-out-the-best-car-for-you</div><div class="line">https://www.peterbe.com/plog/Mvbackupfiles</div><div class="line">...a bunch more...</div><div class="line">https://www.peterbe.com/plog/swedish-holidays-explaine</div><div class="line">https://www.peterbe.com/plog/wing-ide-versus-jed</div><div class="line">https://www.peterbe.com/plog/worst-flash-site-of-the-year-2010</div><div class="line">""".strip().splitlines()</div><div class="line"><span class="keyword">assert</span> len(urls) == <span class="number">100</span></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">download</span><span class="params">(url, data)</span>:</span></div><div class="line">    t0 = time.time()</div><div class="line">    <span class="keyword">assert</span> requests.get(url).status_code == <span class="number">200</span></div><div class="line">    t1 = time.time()</div><div class="line">    data[url] = t1-t0</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(data)</span>:</span></div><div class="line">    <span class="keyword">for</span> url <span class="keyword">in</span> urls:</div><div class="line">        download(url, data)</div><div class="line"></div><div class="line">somemute = &#123;&#125;</div><div class="line">t0 = time.time()</div><div class="line">run(somemute)</div><div class="line">t1 = time.time()</div><div class="line"><span class="keyword">print</span> <span class="string">"TOOK"</span>, t1-t0</div><div class="line"><span class="keyword">print</span> <span class="string">"WOULD HAVE TAKEN"</span>, sum(somemute.values()), <span class="string">"seconds"</span></div></pre></td></tr></table></figure></p>
<p>When run, the output is:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">TOOK 35.3457410336</div><div class="line">WOULD HAVE TAKEN 35.3454759121 seconds</div></pre></td></tr></table></figure></p>
<p>Now, let’s add the deco decorators, so basically these changes:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> deco <span class="keyword">import</span> concurrent, synchronized</div><div class="line"></div><div class="line"><span class="meta">@concurrent</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">download</span><span class="params">(url, data)</span>:</span></div><div class="line">    t0 = time.time()</div><div class="line">    <span class="keyword">assert</span> requests.get(url).status_code == <span class="number">200</span></div><div class="line">    t1 = time.time()</div><div class="line">    data[url] = t1-t0</div><div class="line"></div><div class="line"><span class="meta">@synchronized</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(data)</span>:</span></div><div class="line">    <span class="keyword">for</span> url <span class="keyword">in</span> urls:</div><div class="line">        download(url, data)</div></pre></td></tr></table></figure></p>
<p>And the output this time:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">TOOK 5.13103795052</div><div class="line">WOULD HAVE TAKEN 39.7795288563 seconds</div></pre></td></tr></table></figure></p>
<p>So, instead of it having to take 39.8 seconds it only needed to take 5 seconds with extremely little modification. I call that a win!</p>
<h3>What’s next</h3>

<p>Easy; actually build something that uses this.</p>

      
    </div>
    
  </div>
  
    
    <div class="copyright">
        <p><span>本文标题:</span><a href="/2016/05/17/python-decorated-concurrency/">Python多线程(Decorated concurrency)</a></p>
        <p><span>文章作者:</span><a href="/" title="回到主页">Dragonflyxyz</a></p>
        <p><span>发布时间:</span>2016-05-17, 14:03:48</p>
        <p><span>最后更新:</span>2016-10-17, 14:25:37</p>
        <p>
            <span>原始链接:</span><a class="post-url" href="/2016/05/17/python-decorated-concurrency/" title="Python多线程(Decorated concurrency)">https://dragonflyxyz.github.io/2016/05/17/python-decorated-concurrency/</a>
            <span class="copy-path" data-clipboard-text="原文: https://dragonflyxyz.github.io/2016/05/17/python-decorated-concurrency/　　作者: Dragonflyxyz" title="点击复制文章链接"><i class="fa fa-clipboard"></i></span>
            <script> var clipboard = new Clipboard('.copy-path'); </script>
        </p>
        <p>
            <span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" title="CC BY-NC-SA 4.0 International" target = "_blank">"署名-非商用-相同方式共享 4.0"</a> 转载请保留原文链接及作者。
        </p>
    </div>



    <nav id="article-nav">
        
            <div id="article-nav-newer" class="article-nav-title">
                <a href="/2016/05/20/github-awesome-hadoop/">
                    Hadoop学习资源集合（Github）
                </a>
            </div>
        
        
            <div id="article-nav-older" class="article-nav-title">
                <a href="/2016/05/15/github-awesome-machine-learning/">
                    Github上最全的机器学习资料集合
                </a>
            </div>
        
    </nav>

  
</article>

    <div id="toc" class="toc-article">
        <strong class="toc-title">文章目录</strong>
        
            <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">1.</span> <span class="toc-text">About the order of execution</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">2.</span> <span class="toc-text">@synchronized or .wait()</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">3.</span> <span class="toc-text">No Fire-and-forget</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">4.</span> <span class="toc-text">Number of concurrent workers</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">5.</span> <span class="toc-text">A more realistic example</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">6.</span> <span class="toc-text">One more “realistic” pattern</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#undefined"><span class="toc-number">7.</span> <span class="toc-text">What’s next</span></a></li></ol>
        
    </div>
    <style>
        .left-col .switch-btn,
        .left-col .switch-area {
            display: none;
        }
        .toc-level-3 i,
        .toc-level-3 ol {
            display: none !important;
        }
    </style>

    <input type="button" id="tocButton" value="隐藏目录"  title="点击按钮隐藏或者显示文章目录">

    <script>
        yiliaConfig.toc = ["隐藏目录", "显示目录", !!"false"];
    </script>



    
<div class="share">
    
        <div class="bdsharebuttonbox">
            <a href="#" class="fa fa-twitter bds_twi" data-cmd="twi" title="分享到推特"></a>
            <a href="#" class="fa fa-weibo bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a>
            <a href="#" class="fa fa-qq bds_sqq" data-cmd="sqq" title="分享给 QQ 好友"></a>
            <a href="#" class="fa fa-files-o bds_copy" data-cmd="copy" title="复制网址"></a>
            <a href="#" class="fa fa fa-envelope-o bds_mail" data-cmd="mail" title="通过邮件分享"></a>
            <a href="#" class="fa fa-weixin bds_weixin" data-cmd="weixin" title="生成文章二维码"></a>
            <a href="#" class="fa fa-share-alt bds_more" data-cmd="more"></i></a>
        </div>
        <script>
            window._bd_share_config={
                "common":{"bdSnsKey":{},"bdText":"Python多线程(Decorated concurrency)　| Dragonflyxyz的知识整理　","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"24"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];
        </script>
    

    
</div>







    




    <div class="scroll" id="post-nav-button">
        
            <a href="/2016/05/20/github-awesome-hadoop/" title="上一篇: Hadoop学习资源集合（Github）">
                <i class="fa fa-angle-left"></i>
            </a>
        

        <a title="文章列表"><i class="fa fa-bars"></i><i class="fa fa-times"></i></a>

        
            <a href="/2016/05/15/github-awesome-machine-learning/" title="下一篇: Github上最全的机器学习资料集合">
                <i class="fa fa-angle-right"></i>
            </a>
        
    </div>

    <ul class="post-list"><li class="post-list-item"><a class="post-list-link" href="/2018/02/03/book-c-sharp-deconstructed/">Book《C# Deconstructed-Discover How C# Works on the .Net Framework》</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/03/26/mathematics-rookie-and-machine-learning/">数学菜鸟如何学习机器学习</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/03/23/automatic-sketchup-cn-3/">《Automatic SketchUp Creating 3-D Models in Ruby》中文翻译 11-13章</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/03/10/automatic-sketchup-cn-2/">《Automatic SketchUp Creating 3-D Models in Ruby》中文翻译7-10章</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/03/09/automatic-sketchup-cn-1/">《Automatic SketchUp Creating 3-D Models in Ruby》中文翻译1-5章</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/12/29/python-opencv-learn-note01/">Python OpenCV学习记录</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/11/15/learn-deep-learning-zero-based-1/">零基础入门深度学习(1)-感知器</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/11/15/hexo-show-mathjax-and-table/">Hexo如何显示公式和表格</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/23/hexo-local-search-only-titile/">让Hexo的本地搜索只搜索标题</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/17/why-im-switching-from-python-to-nodejs/">为什么我从Python转战到Node.js</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/16/php-develop-1200examples/">《PHP开发实战1200例》</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/16/introduction-john-carmack/">第一人称射击游戏教父，首款3D游戏的开发者，传奇程序员——约翰·卡马克</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/16/Ebook-search-engine/">几个好的电子书搜索引擎</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/16/MIT-tech-review-2016/">麻省理工科技评论2016年1-6月</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/10/16/head-first-python/">《Head First Python》中文版</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/07/09/microsoft-opensource-malmo-ai/">微软开源Malmo AI项目平台 利用Minecraft构建复杂机器人任务</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/06/22/why-this-robot-in-the-grocery-store/">零售业有福了！大数据机器人卖场值班，点货超轻松</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/06/21/poor-zorro-linux-book/">穷佐罗的Linux书，知识点整理的很好</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/05/20/github-awesome-hadoop/">Hadoop学习资源集合（Github）</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/05/17/python-decorated-concurrency/">Python多线程(Decorated concurrency)</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/05/15/github-awesome-machine-learning/">Github上最全的机器学习资料集合</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/05/12/the-9-best-languages-form-crunching-data/">数据处理的 9 大编程语言</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/05/05/github-awesome-bigdata/">最全的大数据学习资料集合（Github）</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/05/01/a-low-scientist-how-to-get-nobel-prize/">一个逗逼的科研狗是如何成为诺贝尔奖得主的</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/04/23/how-much-a-chip/">一枚芯片的实际成本是多少？</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/04/22/awesome-python-site/">Awesome-python一个收集Python资源的网站</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/04/21/intel-releases-the-arduino-101-firmware-source-code/">Intel将两款arduino设备的实时操作系统开源</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/04/20/studying-the-language-and-structure-in-non-programmers-solutions/">《编程问题的非程序员解决方案之研究》</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/03/26/if-human-and-animals-change/">如果人和动物角色互换？这样的世界怎样想象</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/03/11/checksum-8bits-and-16bits/">checksum-8位和16位校验和代码示例</a></li><li class="post-list-item"><a class="post-list-link" href="/2016/02/06/easy-popular-science-doc-deep-learning/">关于深度学习可能最容易读进去的科普贴</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/12/16/what-is-iot-ppt/">物联网是什么-ppt图</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/12/14/100-BBC-recordfile-parents-with-children/">家长应该陪孩子一起看的100部BBC纪录片</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/11/30/america-people-live-style/">美国日常生活习惯一瞥</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/11/29/photo-wall-style/">教你轻松搞定相片墙壁搭配</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/10/16/algorithm-detail-DES-AES-SHA/">DES、AES、SHA加密算法详解</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/10/16/homedesign-north-america/">越来越喜欢北美的家庭装饰风格</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/03/06/mysql-10millions-optimize-query/">Mysql数据库千万级别数据的查询优化和分页测试</a></li><li class="post-list-item"><a class="post-list-link" href="/2015/02/16/my-blog-set/">博客设置</a></li><li class="post-list-item"><a class="post-list-link" href="/2014/10/01/john-carmack-on-inlined-code/">John Carmack on inlined code</a></li><li class="post-list-item"><a class="post-list-link" href="/2014/05/01/hello-world/">Hello World</a></li><li class="post-list-item"><a class="post-list-link" href="/2014/01/16/test1/">test1</a></li></ul>




    <script>
        
    </script>
</div>
      <footer id="footer">
    <div class="outer">
        <div id="footer-info">
            <div class="footer-left">
                <i class="fa fa-copyright"></i> 
                2016-2018 Dragonflyxyz
            </div>
            <div class="footer-right">
                <a href="http://hexo.io/" target="_blank" title="快速、简洁且高效的博客框架">Hexo</a>  Theme <a href="https://github.com/MOxFIVE/hexo-theme-yelee" target="_blank" title="简而不减 Hexo 双栏博客主题  v3.5">Yelee</a> by MOxFIVE <i class="fa fa-heart animated infinite pulse"></i>
            </div>
        </div>
        
            <div class="visit">
                
                    <span id="busuanzi_container_site_pv" style='display:none'>
                        <span id="site-visit" title="本站到访数"><i class="fa fa-user" aria-hidden="true"></i><span id="busuanzi_value_site_uv"></span>
                        </span>
                    </span>
                
                
                    <span>| </span>
                
                
                    <span id="busuanzi_container_page_pv" style='display:none'>
                        <span id="page-visit"  title="本页阅读量"><i class="fa fa-eye animated infinite pulse" aria-hidden="true"></i><span id="busuanzi_value_page_pv"></span>
                        </span>
                    </span>
                
            </div>
        
    </div>
</footer>
    </div>
    
<script data-main="/js/main.js" src="//cdn.bootcss.com/require.js/2.2.0/require.min.js"></script>

    <script>
        $(document).ready(function() {
            var iPad = window.navigator.userAgent.indexOf('iPad');
            if (iPad > -1 || $(".left-col").css("display") === "none") {
                var bgColorList = ["#9db3f4", "#414141", "#e5a859", "#f5dfc6", "#c084a0", "#847e72", "#cd8390", "#996731"];
                var bgColor = Math.ceil(Math.random() * (bgColorList.length - 1));
                $("body").css({"background-color": bgColorList[bgColor], "background-size": "cover"});
            }
            else {
                var backgroundnum = 5;
                var backgroundimg = "url(/background/bg-x.jpg)".replace(/x/gi, Math.ceil(Math.random() * backgroundnum));
                $("body").css({"background": backgroundimg, "background-attachment": "fixed", "background-size": "cover"});
            }
        })
    </script>





<div class="scroll" id="scroll">
    <a href="#" title="返回顶部"><i class="fa fa-arrow-up"></i></a>
    <a href="#comments" onclick="load$hide();" title="查看评论"><i class="fa fa-comments-o"></i></a>
    <a href="#footer" title="转到底部"><i class="fa fa-arrow-down"></i></a>
</div>
<script>
    // Open in New Window
    
        var oOpenInNew = {
            
            
            
            
            
            
             archives: ".archive-article-title", 
             miniArchives: "a.post-list-link", 
            
             friends: "#js-friends a", 
             socail: ".social a" 
        }
        for (var x in oOpenInNew) {
            $(oOpenInNew[x]).attr("target", "_blank");
        }
    
</script>

<script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js">
</script>
  </div><!-- hexo-inject:begin --><!-- Begin: Injected MathJax -->
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({"tex2jax":{"inlineMath":[["$","$"],["\\(","\\)"]],"skipTags":["script","noscript","style","textarea","pre","code"],"processEscapes":true},"TeX":{"equationNumbers":{"autoNumber":"AMS"}}});
</script>

<script type="text/x-mathjax-config">
  MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
      all[i].SourceElement().parentNode.className += ' has-jax';
    }
  });
</script>

<script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<!-- End: Injected MathJax -->
<!-- hexo-inject:end -->
</body>
</html>